﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.ComponentModel;

namespace Fonlow.BetterRobocopyGui
{
    /// <summary>
    /// Model of a robocopy command line with parameters.
    /// Read a command and initialize a model.
    /// </summary>
    public class RobocopyCommand
    {
        string command = "robocopy";

        public string Command
        {
            get { return command; }
        }

        string source;
        public string Source {
            get
            {
                if (String.IsNullOrEmpty(source))
                    return "Source";
                return source;
            }
            set { source = value; }
        }

        string destination;
        public string Destination
        {
            get
            {
                if (String.IsNullOrEmpty(destination))
                    return "Destination";
                return destination;
            }

            set { destination = value; }
        }

        public string Files { get; set; }

        public Options RobocopyOptions { get; private set; }

        public RobocopyCommand()
        {
            RobocopyOptions = new Options();
            propertiesArray = RobocopyOptions.GetType().GetProperties();
        }

        public RobocopyCommand(Options options)
        {
            RobocopyOptions = options;
            propertiesArray = RobocopyOptions.GetType().GetProperties();
        }

        /// <summary>
        /// Load from a full command line.
        /// </summary>
        /// <param name="commandLine"></param>
        /// <returns></returns>
        public bool Load(string commandLine)
        {
            Regex topSegments = new Regex(Properties.Resources.TOP_SEGMENTS, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Match m = topSegments.Match(commandLine);
            if (m.Success)
            {
                if (m.Groups["Source"].Success)
                {
                    Source = m.Groups["Source"].Value;
                }
                else
                    return false;

                if (m.Groups["Dest"].Success)
                {
                    Destination = m.Groups["Dest"].Value;
                }
                else
                    return false;

                if (m.Groups["Files"].Success)
                {
                    Files = m.Groups["Files"].Value.Trim();
                }

                if (m.Groups["Options"].Success)
                {
                    ResetOptions();
                    ParseOptions(m.Groups["Options"].Value);
                }

                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Command line built from the model.
        /// </summary>
        public string CommandLine
        {
            get
            {
                return command + " " + Arguments;
            }
        }

        public string Arguments
        {
            get
            {
                ParametersBuilder builder = new ParametersBuilder(RobocopyOptions);
                return Source + " " + Destination + " " + Files + " " +
                    builder.GetParametersLine();

            }
        }

        private void ParseOptions(string arguments)
        {
            Regex parametersRegex = new Regex(Properties.Resources.PARAMETERS, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
            MatchCollection matches = parametersRegex.Matches(arguments);
            foreach (Match m in matches)
            {
                if (m.Groups["Name"].Success)
                {
                    string parameterName = m.Groups["Name"].Value;
                    string parameterValue = String.Empty;
                    if (m.Groups["Rest"].Success)
                        parameterValue = m.Groups["Rest"].Value.Trim();

                    AssignParameter(parameterName, parameterValue);
                }
            }
        }

        /// <summary>
        /// All properties of options.
        /// </summary>
        PropertyInfo[] propertiesArray;

        /// <summary>
        /// Get display name of a property
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns>If DisplayNameAttribute does not exist, the property name will be used.</returns>
        static string GetDisplayName(PropertyInfo propertyInfo)
        {
            object[] objects = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false);
            if (objects.Length == 1)
            {
                return (objects[0] as DisplayNameAttribute).DisplayName;
            }
            return propertyInfo.Name;
        }

        /// <summary>
        /// Get default value of a property.
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns>Null if the default value is not defined. Empty string if the type is string. 0 if the type is int.</returns>
        static object GetDefaultValue(PropertyInfo propertyInfo)
        {
            object[] objects = propertyInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
            if (objects.Length == 1)
            {
                return (objects[0] as DefaultValueAttribute).Value;
            }

            if (propertyInfo.PropertyType == typeof(string))
            {
                return String.Empty;
            }

            if (propertyInfo.PropertyType == typeof(int))
            {
                return 0;
            }

            return null;
        }

        static string GetHelp(PropertyInfo propertyInfo)
        {
            object[] objects = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (objects.Length == 1)
            {
                return (objects[0] as DescriptionAttribute).Description;
            }
            return null;
        }

        void ResetOptions()
        {
            foreach (PropertyInfo propertyInfo in propertiesArray)
            {
                propertyInfo.SetValue(RobocopyOptions, GetDefaultValue(propertyInfo), null);
            }

        }

        /// <summary>
        /// This is for DCOPY:T which format is inconsistent against all others.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        static string TrimColumnRight(string s)
        {
            string[] ss = s.Split(new char[] { ':' });
            return ss[0];
        }

        bool AssignParameter(string parameterName, string parameterRest)
        {
            foreach (PropertyInfo propertyInfo in propertiesArray)
            {
                if (parameterName.Equals(TrimColumnRight(GetDisplayName(propertyInfo)), StringComparison.CurrentCultureIgnoreCase))
                {
                    if (propertyInfo.PropertyType == typeof(bool))
                    {
                        propertyInfo.SetValue(RobocopyOptions, true, null);
                    }
                    else if (propertyInfo.PropertyType == typeof(string))
                    {
                        propertyInfo.SetValue(RobocopyOptions, parameterRest, null);
                    }
                    else if (propertyInfo.PropertyType == typeof(int))
                    {
                        int intP;
                        if (Int32.TryParse(parameterRest, out intP))
                            propertyInfo.SetValue(RobocopyOptions, intP, null);
                        else
                            System.Diagnostics.Trace.WriteLine(String.Format("Parameter {0} has invalid value {1}", parameterName, parameterRest));
                    }
                    else if (propertyInfo.PropertyType == typeof(CopyFlags))
                    {
                        CopyFlagsConverter converter = new CopyFlagsConverter();
                        CopyFlags flags = (CopyFlags)converter.ConvertFromString(parameterRest);
                        if (flags != CopyFlags.None)
                        {
                            propertyInfo.SetValue(RobocopyOptions, converter.ConvertFromString(parameterRest), null);
                        }
                        else
                            System.Diagnostics.Trace.WriteLine(String.Format("Parameter {0} has invalid value {1}", parameterName, parameterRest));
                    }
                    else if (propertyInfo.PropertyType == typeof(Rashcneto))
                    {
                        RashcnetoFlagsConverter converter = new RashcnetoFlagsConverter();
                        Rashcneto flags = (Rashcneto)converter.ConvertFromString(parameterRest);
                        if (flags != Rashcneto.None)
                        {
                            propertyInfo.SetValue(RobocopyOptions, converter.ConvertFromString(parameterRest), null);
                        }
                        else
                            System.Diagnostics.Trace.WriteLine(String.Format("Parameter {0} has invalid value {1}", parameterName, parameterRest));
                    }
                    else
                        propertyInfo.SetValue(RobocopyOptions, parameterRest, null);

                    return true;
                }
            }

            return false;
        }

        public string FindParameterHelp(string parameterName)
        {
            foreach (PropertyInfo propertyInfo in propertiesArray)
            {
                if (parameterName.Equals(TrimColumnRight(GetDisplayName(propertyInfo)), StringComparison.CurrentCultureIgnoreCase))
                {
                    return GetHelp(propertyInfo);
                }

            }
            return null;
        }
    }
}
